package jus.aor.talk.v3;

import java.net.InetAddress;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import jus.aor.talk.Message;
import jus.aor.talk.MessageSay;
import jus.aor.talk.MessageStop;
import jus.aor.talk.MessageWho;
import jus.aor.talk.ui.server.ServerComposant;
import jus.aor.talk.ui.server.ServerEventListener;
import jus.aor.talk.ui.server.ServerEventWarning;
import jus.util.assertion.Require;

/**
* L'interface _Forum définit les primtives d'un forum de discussion.
* @version 	1.0, 30/11/98
* @author	Philippe Morat
* @email	Philippe.Morat@imag.fr
* @see		Intervenant
*/
public class Forum extends UnicastRemoteObject implements ServerComposant, jus.aor.talk.v3.ForumInterface {
	/**
	 * 
	 */
	private static final long serialVersionUID = 389690304147251005L;
	protected List<ServerEventListener> serverListeners = new LinkedList<ServerEventListener>();
	public void addServerEventListener(ServerEventListener s) { serverListeners.add(s);}
	public void removeServerEventListener(ServerEventListener s) { serverListeners.remove(s);}
	public void fireWarning(String w) { ServerEventWarning e = new ServerEventWarning(this,w); for(ServerEventListener s : serverListeners) s.warning(e);}

	/** Descripteur d'un intervenant dans le forum de discussion*/
	public class IntervenantDescriptor {
		/** le prenom de l'intervenant */
		public String prenom;
		/** le nom de l'intervenant */
		public String nom;
		/** l'objet représentant l'intervenant */
		public IntervenantInterface intervenant;
		/**
		* constructeur d'un descripteur d'intervenant
		* <dt><b> Requires: </b><code>
		* <dd>  argsValides : intervenant!=null && prenom!=null && nom!=null
		* </code>
		*/
		public IntervenantDescriptor(IntervenantInterface intervenant, String prenom, String nom) throws jus.util.assertion.Require {
			if(intervenant==null || prenom==null || nom==null) {throw new jus.util.assertion.Require(this.getClass()+" : IntervenantDescriptor");}
			this.intervenant=intervenant;
			this.prenom=prenom;
			this.nom=nom;
		}
	}
	/** le nom attribué à ce forum*/
	protected String nom;
	/**la structure de mémoristion des intervenants*/
	protected  transient Map<Integer,IntervenantDescriptor> intervenants = new HashMap<Integer,IntervenantDescriptor>();
	/**l'identifiant unique d'intervenant*/	
	protected Integer id = new Integer(0);
	/** constructeur d'un forum*/
	public Forum() throws RemoteException {intervenants = new HashMap<Integer,IntervenantDescriptor>(); nom ="";}
	/**
	* abonnement au forum de discussion
	* @requireintervenantValide : intervenant!=null
	* @require etatValide : this.intervenants!=null && !intervenants.containsValue(intervenant)
	*/
	public Integer enter(IntervenantInterface intervenant, String prenom, String nom) throws jus.util.assertion.Require, java.rmi.RemoteException {
		if(intervenant==null || this.intervenants==null || intervenants.containsValue(intervenant)) {throw new jus.util.assertion.Require(this.getClass()+" : enter");}
		for(IntervenantDescriptor desc1 : intervenants.values()){
			if(desc1.nom.equals(nom)&& desc1.prenom.equals(prenom)){
				throw new jus.util.assertion.Require("Utilisateur déjà présent, choississez un autre speudo");
			}
		}
		id = new Integer(id.intValue()+1);
		intervenants.put(id,new IntervenantDescriptor(intervenant,prenom,nom));

		MessageSay m = new MessageSay("Forum ","(" + this.nom + ")", prenom + " " + nom + " joined.");
		fireWarning(new Date()+" ---> enter : "+nom+", "+prenom);
		for(IntervenantDescriptor dest : intervenants.values())
		{
			dest.intervenant.listen(m);
		}
		return id;
	}
	/**
	* résiliation du forum de discussion
	* @resuire idValide : this.id!=null && intervenants.containsKey(id);
	* @require etatValide : this.forum!=null
	*/
	public synchronized boolean leave(Integer id) throws jus.util.assertion.Require, java.rmi.RemoteException {
		IntervenantDescriptor idesc = intervenants.get(id);
		if(this.intervenants==null || id==null || !intervenants.containsKey(id)) {
			return false;
		}
		intervenants.remove(id);
		MessageSay m = new MessageSay("(" + this.nom + ")", "Forum ", idesc.prenom + " " + idesc.nom + " left.");
		fireWarning(new Date()+" ---> leave : "+idesc.nom+", "+idesc.prenom);
		for(IntervenantDescriptor dest : intervenants.values())
		{
			dest.intervenant.listen(m);
		}
		return true;
	}
	/**
	* envoi d'une participation au forum de discussion
	* @require participationValide : pariticpation!=null
	* @require idValide : this.id!=null && intervenants.containsKey(id);
	* @require etatValide : this.forum!=null
	*/
	public synchronized void say(Integer id, String participation) throws jus.util.assertion.Require, java.rmi.RemoteException {
		IntervenantDescriptor idesc = intervenants.get(id);
		if(this.intervenants==null || id==null || !intervenants.containsKey(id) || participation==null) {
			throw new jus.util.assertion.Require(this.getClass()+" : say");
		}
		IntervenantDescriptor intervenant = intervenants.get(id);
		MessageSay m = new MessageSay(intervenant.prenom,intervenant.nom,participation);

		fireWarning(new Date()+" ---> say : "+idesc.nom+", "+idesc.prenom + " said : " + m.message);
		for(IntervenantDescriptor dest : intervenants.values())
		{
			dest.intervenant.listen(m);
		}
	}
	/**
	* demande de la liste des participants au forum de discussion
	* @require idValide : this.id!=null && intervenants.containsKey(id);
	* @require etatValide : this.forum!=null
	*/
	public synchronized Message who(Integer id) throws jus.util.assertion.Require, java.rmi.RemoteException {
		IntervenantDescriptor idesc = intervenants.get(id);
		if(this.intervenants==null || id==null || !intervenants.containsKey(id)) {throw new jus.util.assertion.Require(this.getClass()+" : who");}
		String s = "";
		for(IntervenantDescriptor dest : intervenants.values()){s+=dest.prenom+"."+dest.nom+"\n";}
		fireWarning(new Date()+" ---> who : "+idesc.nom+", "+idesc.prenom);
		return new MessageWho(nom,s);	
	}
	/* (non-Javadoc)
	 * @see jus.aor.talk.ui.server.ServerComposant#start(java.lang.String, java.lang.String)
	 */
	public void start(String[] args) {
		this.nom=args[0];
		try {
			System.setSecurityManager(new RMISecurityManager());
			Naming.rebind("//"+ InetAddress.getLocalHost().getHostAddress() +"/"+ this.nom, this);
			fireWarning("Le Forum " + nom + " a ete enregistre sous //"+ InetAddress.getLocalHost().getHostAddress() +"/"+ this.nom);
		}catch(Exception x) {
			System.out.println(x);
			System.exit(-1);
		}
	}
	/* (non-Javadoc)
	 * @see jus.aor.talk.ui.server.ServerComposant#stop()
	 */
	public void stop() {
		MessageStop m = new MessageStop();
		fireWarning(new Date()+" ---> stop : ");
		for(IntervenantDescriptor dest : intervenants.values()){
			try {
				dest.intervenant.stop(m);
			}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
		intervenants.clear();
		try {
			Naming.unbind("//"+ InetAddress.getLocalHost().getHostAddress() +"/"+ this.nom);
			fireWarning("Le Forum "+ nom +" a ete arrete.");
		}
		catch(Exception x) {
			fireWarning(x.getMessage());
		}
	}
}

